/**
 * 
 */
package org.oproject.framework.orm.ibatis.bytecode;

import java.lang.reflect.Method;
import java.util.Collection;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.oproject.framework.orm.ibatis.bytecode.codegenerator.method.MethodGenerator;
import org.oproject.framework.orm.ibatis.bytecode.codegenerator.property.BeanPropertyAndSetterMethodGenerator;

/**
 * <p>
 * 动态数据访问实现类构造器
 * </p>
 * @author aohai.li
 * @version dynamicIbatis, 2010-11-9
 * @since v1
 */
public class DynamicDAOImplClassGenerator implements Opcodes {

	/**
	 * 接口名称，以.分割包和类
	 */
	private String interfaceName = null;
	
	/**
	 * 接口名称路径，以/分割包和类
	 */
	private String interfaceNamePath = null;
	
	/**
	 * 
	 */
	private String targetClassName = null;
	
	/**
	 * 实现接口的目标类路径，以/分割包和类
	 */
	private String targetClassNamePath = null;
	
	/**
	 * 类节点
	 */
	private ClassNode classNode = new ClassNode();
	
	/**
	 * 接口类型信息
	 */
	@SuppressWarnings("unchecked")
	private Class interfaceClass = null;
	
	/**
	 * 接口声明的方法集合
	 */
	private Method[] interfaceMethods = null;
	
	private Class<?> sqlClientClass;
	
	private String sqlClientProperty;
	
	/**
	 * 默认构造函数
	 * @param interfaceName 数据访问接口
	 */
	public DynamicDAOImplClassGenerator(String interfaceName, String newClassName){
		this.interfaceName = interfaceName;
		this.interfaceNamePath = interfaceName.replaceAll("\\.", "/");
		this.targetClassName = newClassName;
		this.targetClassNamePath = targetClassName.replaceAll("\\.", "/");;
	}
	
	public void defineClass() throws Exception{
		ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		this.classNode.accept(cw);
		net.sf.cglib.core.ReflectUtils.defineClass(targetClassName, cw.toByteArray(), Thread.currentThread().getContextClassLoader());
	}
	
	public void initClass() throws Exception{
		
		// 从定义的接口参数中获取接口信息，并对接口进行验证
		setInterfaceInfo();
		
		// 创建类信息
		createClassInfo();
		
		// 创建构造函数
		createMethodConstruct();
	}
	
	/**
	 * 读取接口信息，并且进行验证
	 * @throws ClassNotFoundException 当ClassLoader无法装载接口类型的时候，抛出；
	 * @exception IllegalArgumentException 当接口名称不是以"DAO"结尾或者接口参数类型不是一个Interface类
	 * 型的时候，抛出该异常。
	 */
	private void setInterfaceInfo() throws ClassNotFoundException{
		
		// 如果参数接口不是以DAO结尾，则抛出异常。
		if(!interfaceName.endsWith("DAO")){
			throw new IllegalArgumentException(interfaceName + " is not end with 'DAO'.");
		}
		
		// 加载接口接口名称
		interfaceClass = Thread.currentThread().getContextClassLoader().loadClass(interfaceName);
		
		// 如果参数接口不是接口，抛出异常。
		if(!interfaceClass.isInterface()){
			throw new IllegalArgumentException(interfaceName + " is not a Interface.");
		}
		
		// 接口方法
		this.interfaceMethods = interfaceClass.getMethods();
	}
	
	/**
	 * 创建类，并且设置该类的基础信息
	 */
	@SuppressWarnings("unchecked")
	private void createClassInfo() {
		this.classNode.version = V1_6;
		this.classNode.access = ACC_PUBLIC;
		this.classNode.name = targetClassNamePath;
		this.classNode.superName = "java/lang/Object";
		this.classNode.interfaces.add(interfaceNamePath);
	}
	
	/**
	 * 创建类的构造函数
	 */
	@SuppressWarnings("unchecked")
	private void createMethodConstruct() {
		MethodNode mn_construct = new MethodNode(ACC_PUBLIC, "<init>", "()V", null, null);
		InsnList li = mn_construct.instructions;
		li.add(new VarInsnNode(ALOAD, 0));
		li.add(new MethodInsnNode(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"));
		li.add(new InsnNode(RETURN));
		mn_construct.maxLocals = 0;
		mn_construct.maxStack = 0;
		this.classNode.methods.add(mn_construct);
	}
	
	/**
	 * 创建属性以及对应的getter、setter方法
	 * @param propertyClass
	 */
	public void addSqlMapClientProperty(Class<?> propertyClass, String propertyName){
		this.sqlClientClass = propertyClass;
		this.sqlClientProperty = propertyName;
		new BeanPropertyAndSetterMethodGenerator().generate(this.classNode, propertyClass, propertyName);
	}
	
	/**
	 * 实现接口方法
	 * @param methodGenerators
	 */
	public void addImplementsMethod(Collection<MethodGenerator> methodGenerators){
		for(Method method: interfaceMethods){
			boolean setMethodSuccess = false;
			for(MethodGenerator methodGenerator : methodGenerators){
				if(methodGenerator.validate(method)){
					methodGenerator.generate(classNode, method, sqlClientProperty, sqlClientClass);
					setMethodSuccess = true;
					break;
				}
			}
			if(!setMethodSuccess){
				throw new IllegalArgumentException("没有找到适合的方法处理器:" + method.getName());
			}
			
		}
	}
}
